﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace WebParser
{
    public class HttpContentParser
    {
        public event EventHandler<ContentEventArgs> ContentReceived;
        protected virtual void OnContentReceiver(ContentEventArgs e)
        {
            if (ContentReceived != null) ContentReceived(this, e);
        }

        private readonly string _boundary;
        private string _contentType;
        private int _contentLength;
        private readonly Encoding _enc = Encoding.ASCII;
        private readonly List<byte> _tmp;
        private Action<Stream> _p;

        public HttpContentParser(string boundary)
        {
            _boundary = boundary;
            _tmp = new List<byte>();
            Reset();
        }

        public void Parse(Stream b)
        {
            _p(b);
        }
        public void Reset()
        {
            _tmp.Clear();
            SwitchTo(ReadBoundary);
        }
        private void SwitchTo(Action<Stream> function)
        {
            //var source = (_p == null) ? "{EMPTY}" : _p.Method.Name;
            //var target = (function == null) ? "{EMPTY}" : function.Method.Name;
            //Debug.WriteLine(string.Format("{0} => {1}", source, target));
            _p = function;
        }
        private void ReadBoundary(Stream stream)
        {
            while (true)
            {
                var b = stream.ReadByte();
                if (b < 0) return;
                if (b == '\r') continue;
                if (b == '\n')
                {
                    var line = _enc.GetString(_tmp.ToArray());
                    if (line == _boundary)
                        SwitchTo(ReadFrameHeader);
                    _tmp.Clear();
                    return;
                }
                _tmp.Add((byte)b);
            }
        }
        private void ReadFrameHeader(Stream stream)
        {
            while (true)
            {
                var b = stream.ReadByte();
                if (b < 0) return;
                if (b == '\r') continue;
                if (b == '\n')
                {
                    if (_tmp.Count < 1)
                    {
                        SwitchTo(ReadFrameData);
                        return;
                    }
                    var line = _enc.GetString(_tmp.ToArray());
                    var index = line.IndexOf(": ");
                    if (index > -1)
                    {
                        var key = line.Substring(0, index);
                        switch (key)
                        {
                            case "Content-Type":
                                _contentType = line.Substring(index + 2);
                                break;
                            case "Content-Length":
                                int len;
                                if (int.TryParse(line.Substring(index + 2), out len))
                                    _contentLength = len;
                                break;
                        }
                    }
                    _tmp.Clear();
                    continue;
                }
                _tmp.Add((byte)b);
            }
        }
        private readonly byte[] _buf = new byte[0xFFFFFF];
        private void ReadFrameData(Stream stream)
        {
            var readed = 0;
            do
            {
                readed += stream.Read(_buf, readed, _contentLength - readed);
            } while (readed < _contentLength);
            var buffer = new byte[readed];
            Buffer.BlockCopy(_buf, 0, buffer, 0, readed);
            var e = new ContentEventArgs(buffer, _contentType, _contentLength);
            OnContentReceiver(e);

            _contentType = null;
            _contentLength = -1;

            _tmp.Clear();
            SwitchTo(ReadBoundary);
        }

    }
}